package syncer.client;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import rmi.Client;
import rmi.sync.FieldTuple;
import syncer.ObjectContentCopyer;
import syncer.client.bitmap.BitMap;

public class SyncClient implements Runnable {

	public static boolean DEBUG = true;
	private static SyncClient instance = new SyncClient();
	private HashMap<Integer, Object> syncList = new HashMap<Integer, Object>();
	private Client client = new Client();
	private long clock = System.currentTimeMillis();
	private long inteval = 5000;

	private ArrayList<Integer> syncIds = new ArrayList<Integer>();// id list to be sync-ed
	private Map<Integer, Integer> syncOrder = new HashMap<Integer, Integer>();// (id,index)-using an Id to get its index in syncIds

	private SyncClient() {

	}

	public static SyncClient getInstance() {
		return instance;
	}

	public Object insertObj(Object obj) {
		if (obj == null) {
			if (DEBUG) {
				System.out.println("null Object!");
			}
			return null;
		}
		if (syncList.containsKey(obj.hashCode())) {
			if (DEBUG) {
				System.out.println(obj.toString() + "exsisted!!");
			}
			return null;
		}
		setObject(obj.hashCode(), obj);

		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field f : fields) {
			f.setAccessible(true);
			if (f.getType() == null)
				continue;
			try {
				Object var = f.get(obj);
				if (DEBUG) {
					System.out.println(var);
				}
				insertObj(var);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return obj;
	}

	public Object getById(int id) {
		return syncList.get(id);
	}

	public void setObject(int id, Object obj) {
		syncList.put(id, obj);
		syncIds.add(id);
		syncOrder.put(id, syncIds.size() - 1);
	}

	/***
	 * read object of id from syncServer
	 * 
	 * @param id
	 */
	public Object readRemote(int id) {
		Object obj = client.readRemote(id);
		if (getById(id) != null) {
			ObjectContentCopyer.copyTo(obj, getById(id));
			obj = getById(id);
		} else {
			setObject(id, obj);
		}
		return obj;
	}

	/***
	 * write (id,obj) to syncServer
	 * 
	 * @param id
	 * @param obj
	 */
	public void writeRemote(int id, Object obj) {
		client.writeRemote(id, obj);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

	public void run() {
		// TODO Auto-generated method stub
		long curTime = System.currentTimeMillis();
		if (curTime - clock > inteval) {
			int objNum = syncList.size();
			BitMap bitmap = new BitMap(objNum);

			// bitmap may be changed in the process
			for (int i = 0; i < bitmap.legalBitLength(); i++) {
				if (bitmap.getBitOf(i) == 1)
					continue;// 1 means visited, 0 means un-visited
				int id = syncIds.get(i);
				List<FieldTuple> fieldSet = client.readContent(id);
				syncToList(fieldSet, id, bitmap);
			}

			clock = System.currentTimeMillis();
		}
	}

	/***
	 * assuming syncList[id] exists
	 * 
	 * @param fieldSet of the object of the given id
	 * @param id
	 */
	private void syncToList(List<FieldTuple> fieldSet, int id, BitMap bitmap) {
		if (bitmap.getBitOf(syncOrder.get(id)) == 1)
			return;
		bitmap.setBitTo1(syncOrder.get(id));// marked as visited

		Object rootInList = syncList.get(id);
		for (FieldTuple tuple : fieldSet) {
			Integer fId = tuple.getfId();
			if (fId == null) {
				// if the field is primitive or String
				try {
					rootInList.getClass().getField(tuple.getfName()).set(
							rootInList, tuple.getfValue());
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NoSuchFieldException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				// if the field is an Object reference
				try {
					if (!syncList.containsKey(fId)) {
						Class<?> type = tuple.getfType();
						Object newObj = type.newInstance();// need a default constructor
						setObject(fId, newObj);
						bitmap.insertNewBit();
					}
					Object ref = syncList.get(fId);
					rootInList.getClass().getField(tuple.getfName()).set(rootInList, ref);

					List<FieldTuple> subSet = client.readContent(fId);
					syncToList(subSet, fId, bitmap);
				} catch (InstantiationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NoSuchFieldException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}
